Interactive graphics with plotly
install.packages("plotly")
First Interactive Plot
library(tidyverse)
library(plotly)
p <- ggplot(data = midwest) +
geom_point(mapping = aes(x = popdensity, y = percollege))
ggplotly(p)
Customized Interactive Plot
p <- ggplot(midwest,
aes(x = popdensity, y = percollege, color = state)) +
geom_point() +
scale_x_continuous("Population Density",
breaks = seq(0, 80000, 20000)) +
scale_y_continuous("Percent College Graduates") +
scale_color_discrete("State") +
theme_bw()
ggplotly(p)
Your Turn
- Using the
starwars data, create a static ggplot and use the ggplotly function to turn it interactive.
Lord of the Rings Data
lotr <- read_tsv('https://raw.githubusercontent.com/jennybc/lotr/master/lotr_clean.tsv')
Parsed with column specification:
cols(
Film = [31mcol_character()[39m,
Chapter = [31mcol_character()[39m,
Character = [31mcol_character()[39m,
Race = [31mcol_character()[39m,
Words = [32mcol_double()[39m
)
lotr
Create plotly by hand
plot_ly(lotr, x = ~Words) %>% add_histogram()
Subplots
one_plot <- function(d) {
plot_ly(d, x = ~Words) %>%
add_histogram() %>%
add_annotations(
~unique(Film), x = 0.5, y = 1,
xref = "paper", yref = "paper", showarrow = FALSE
)
}
lotr %>%
split(.$Film) %>%
lapply(one_plot) %>%
subplot(nrows = 1, shareX = TRUE, titleX = FALSE) %>%
hide_legend()
Grouped bar plot
plot_ly(lotr, x = ~Race, color = ~Film) %>% add_histogram()
Plot of proportions
## number of diamonds by cut and clarity (n)
lotr_count <- count(lotr, Race, Film)
## number of diamonds by cut (nn)
lotr_prop <- left_join(lotr_count, count(lotr_count, Race, wt = n),
by = 'Race')
lotr_prop %>%
mutate(prop = n.x / n.y) %>%
plot_ly(x = ~Race, y = ~prop, color = ~Film, width = 900) %>%
add_bars() %>%
layout(barmode = "stack")
Your Turn
- Using the
gss_cat data, create a histrogram for the tvhours variable.
- Using the
gss_cat data, create a bar chart showing the partyid variable by the marital status.
Scatterplots by Hand
plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
add_markers()
Change symbol
plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
add_markers(symbol = ~state)
Change color
plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
add_markers(color = ~state, colors = viridis::viridis(5))
Line Graph
storms_yearly <- storms %>%
group_by(year) %>%
summarise(num = length(unique(name)))
plot_ly(storms_yearly, x = ~year, y = ~num) %>%
add_lines()
Your Turn
- Using the
gss_cat data, create a scatterplot showing the age and tvhours variables.
- Compute the average time spent watching tv by year and marital status. Then, plot the average time spent watching tv by year and marital status.
Highcharter; Highcharts for R
install.packages("highcharter")
hchart function
library(highcharter)
Registered S3 method overwritten by 'xts':
method from
as.zoo.xts zoo
Registered S3 method overwritten by 'quantmod':
method from
as.zoo.data.frame zoo
Highcharts (www.highcharts.com) is a Highsoft software product which is
not free for commercial and Governmental use
lotr_count <- lotr %>%
count(Film, Race)
hchart(lotr_count, "column", hcaes(x = Race, y = n, group = Film))
A second hchart
hchart(midwest, "scatter", hcaes(x = popdensity, y = percollege, group = state))
Histogram
hchart(lotr$Words)
Your Turn
- Using the
hchart function, create a bar chart or histogram with the gss_cat data.
- Using the
hchart function, create a scatterplot with the gss_cat data.
Build Highcharts from scratch
hc <- highchart() %>%
hc_xAxis(categories = lotr_count$Race) %>%
hc_add_series(name = 'The Fellowship Of The Ring',
data = filter(lotr_count, Film == 'The Fellowship Of The Ring')$n) %>%
hc_add_series(name = 'The Two Towers',
data = filter(lotr_count, Film == 'The Two Towers')$n) %>%
hc_add_series(name = 'The Return Of The King',
data = filter(lotr_count, Film == 'The Return Of The King')$n)
hc
Change Chart type
hc <- hc %>%
hc_chart(type = 'column')
hc
Change Colors
hc <- hc %>%
hc_colors(substr(viridis::viridis(3), 0, 7))
hc
Modify Axes
hc <- hc %>%
hc_xAxis(title = list(text = "Race")) %>%
hc_yAxis(title = list(text = "Number of Words Spoken"),
showLastLabel = FALSE)
hc
Add title, subtitle, move legend
hc <- hc %>%
hc_title(text = 'Number of Words Spoken in Lord of the Rings Films',
align = 'left') %>%
hc_subtitle(text = 'Broken down by <i>Film</i> and <b>Race</b>',
align = 'left') %>%
hc_legend(align = 'right', verticalAlign = 'top', layout = 'vertical',
x = 0, y = 80) %>%
hc_exporting(enabled = TRUE)
hc
Your Turn
- Build up a plot from scratch, getting the figure close to publication quality using the
gss_cat data.
Correlation Matrices
select(storms, wind, pressure, ts_diameter, hu_diameter) %>%
cor(use = "pairwise.complete.obs") %>%
hchart()
Leaflet Example
library(leaflet)
storms %>%
filter(name %in% c('Ike', 'Katrina'), year > 2000) %>%
leaflet() %>%
addTiles() %>%
addCircles(lng = ~long, lat = ~lat, popup = ~name, weight = 1,
radius = ~wind*1000)
gganimate
install.packages("gganimate")
library(gganimate)
ggplot(storms, aes(x = pressure, y = wind, color = status)) +
geom_point(show.legend = FALSE) +
xlab("Pressure") +
ylab("Wind Speed (MPH)") +
facet_wrap(~status) +
theme_bw(base_size = 14) +
labs(title = 'Year: {frame_time}') +
transition_time(as.integer(year)) +
ease_aes('linear')
anim_save(filename = 'storms.gif')

LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIC0gSW50ZXJhY3RpdmUgR3JhcGhpY3MgdXNpbmcgUiIKYXV0aG9yOiAiQnJhbmRvbiBMZUJlYXUiCmRhdGU6ICJKdW5lIDEyLCAyMDE5IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cF9jaHVua3MsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02LCBmaWcuY2FwID0gTlVMTCkgCmBgYAoKIyBJbnRlcmFjdGl2ZSBncmFwaGljcyB3aXRoIHBsb3RseQpgYGB7ciBpbnN0YWxsLCBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInBsb3RseSIpCmBgYAoKIyBGaXJzdCBJbnRlcmFjdGl2ZSBQbG90CmBgYHtyIGZpcnN0X3Bsb3RseSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGxvdGx5KQpwIDwtIGdncGxvdChkYXRhID0gbWlkd2VzdCkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gcG9wZGVuc2l0eSwgeSA9IHBlcmNvbGxlZ2UpKQpnZ3Bsb3RseShwKQpgYGAKCiMgQ3VzdG9taXplZCBJbnRlcmFjdGl2ZSBQbG90CmBgYHtyIGN1c3RvbV9wbG90bHksIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9CnAgPC0gZ2dwbG90KG1pZHdlc3QsIAogICAgICAgYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSwgY29sb3IgPSBzdGF0ZSkpICsKICBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoIlBvcHVsYXRpb24gRGVuc2l0eSIsIAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgODAwMDAsIDIwMDAwKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoIlBlcmNlbnQgQ29sbGVnZSBHcmFkdWF0ZXMiKSArIAogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKCJTdGF0ZSIpICsgCiAgdGhlbWVfYncoKQpnZ3Bsb3RseShwKQpgYGAKCiMgWW91ciBUdXJuCjEuIFVzaW5nIHRoZSBgc3RhcndhcnNgIGRhdGEsIGNyZWF0ZSBhIHN0YXRpYyBnZ3Bsb3QgYW5kIHVzZSB0aGUgYGdncGxvdGx5YCBmdW5jdGlvbiB0byB0dXJuIGl0IGludGVyYWN0aXZlLiAKCiMgTG9yZCBvZiB0aGUgUmluZ3MgRGF0YQotIERhdGEgZnJvbSBKZW5ueSBCcnlhbjogPGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2xvdHI+CgpgYGB7ciByZWFkX2luX2xvdHIsIGVycm9yID0gRkFMU0V9CmxvdHIgPC0gcmVhZF90c3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qZW5ueWJjL2xvdHIvbWFzdGVyL2xvdHJfY2xlYW4udHN2JykKbG90cgpgYGAKCiMgQ3JlYXRlIHBsb3RseSBieSBoYW5kCmBgYHtyIHBsb3RseV9ieV9oYW5kLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KcGxvdF9seShsb3RyLCB4ID0gfldvcmRzKSAlPiUgYWRkX2hpc3RvZ3JhbSgpCmBgYAoKIyBTdWJwbG90cwpgYGB7ciBzdWJwbG90cywgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9Cm9uZV9wbG90IDwtIGZ1bmN0aW9uKGQpIHsKICBwbG90X2x5KGQsIHggPSB+V29yZHMpICU+JQogICAgYWRkX2hpc3RvZ3JhbSgpICU+JQogICAgYWRkX2Fubm90YXRpb25zKAogICAgICB+dW5pcXVlKEZpbG0pLCB4ID0gMC41LCB5ID0gMSwgCiAgICAgIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgc2hvd2Fycm93ID0gRkFMU0UKICAgICkKfQoKbG90ciAlPiUKICBzcGxpdCguJEZpbG0pICU+JQogIGxhcHBseShvbmVfcGxvdCkgJT4lIAogIHN1YnBsb3QobnJvd3MgPSAxLCBzaGFyZVggPSBUUlVFLCB0aXRsZVggPSBGQUxTRSkgJT4lCiAgaGlkZV9sZWdlbmQoKQpgYGAKCgojIEdyb3VwZWQgYmFyIHBsb3QKYGBge3IgcGxvdGx5X2dyb3VwLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KcGxvdF9seShsb3RyLCB4ID0gflJhY2UsIGNvbG9yID0gfkZpbG0pICU+JSBhZGRfaGlzdG9ncmFtKCkKYGBgCgojIFBsb3Qgb2YgcHJvcG9ydGlvbnMKYGBge3IgcGxvdGx5X3Byb3BvcnRpb25zLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2fQojIyBudW1iZXIgb2YgZGlhbW9uZHMgYnkgY3V0IGFuZCBjbGFyaXR5IChuKQpsb3RyX2NvdW50IDwtIGNvdW50KGxvdHIsIFJhY2UsIEZpbG0pCiMjIG51bWJlciBvZiBkaWFtb25kcyBieSBjdXQgKG5uKQpsb3RyX3Byb3AgPC0gbGVmdF9qb2luKGxvdHJfY291bnQsIGNvdW50KGxvdHJfY291bnQsIFJhY2UsIHd0ID0gbiksIAogICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gJ1JhY2UnKQoKbG90cl9wcm9wICU+JQogIG11dGF0ZShwcm9wID0gbi54IC8gbi55KSAlPiUKICBwbG90X2x5KHggPSB+UmFjZSwgeSA9IH5wcm9wLCBjb2xvciA9IH5GaWxtLCB3aWR0aCA9IDkwMCkgJT4lCiAgYWRkX2JhcnMoKSAlPiUKICBsYXlvdXQoYmFybW9kZSA9ICJzdGFjayIpCmBgYAoKIyBZb3VyIFR1cm4KMS4gVXNpbmcgdGhlIGBnc3NfY2F0YCBkYXRhLCBjcmVhdGUgYSBoaXN0cm9ncmFtIGZvciB0aGUgYHR2aG91cnNgIHZhcmlhYmxlLiAKMi4gVXNpbmcgdGhlIGBnc3NfY2F0YCBkYXRhLCBjcmVhdGUgYSBiYXIgY2hhcnQgc2hvd2luZyB0aGUgYHBhcnR5aWRgIHZhcmlhYmxlIGJ5IHRoZSBgbWFyaXRhbGAgc3RhdHVzLgoKIyBTY2F0dGVycGxvdHMgYnkgSGFuZApgYGB7ciBwbG90bHlfc2NhdHRlciwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDYsIHdhcm5pbmcgPSBGQUxTRX0KcGxvdF9seShtaWR3ZXN0LCB4ID0gfnBvcGRlbnNpdHksIHkgPSB+cGVyY29sbGVnZSkgJT4lCiAgYWRkX21hcmtlcnMoKQpgYGAKCiMgQ2hhbmdlIHN5bWJvbApgYGB7ciBwbG90bHlfc3ltYm9sLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KcGxvdF9seShtaWR3ZXN0LCB4ID0gfnBvcGRlbnNpdHksIHkgPSB+cGVyY29sbGVnZSkgJT4lCiAgYWRkX21hcmtlcnMoc3ltYm9sID0gfnN0YXRlKQpgYGAKCiMgQ2hhbmdlIGNvbG9yCmBgYHtyIHBsb3RseV9jb2xvciwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDl9CnBsb3RfbHkobWlkd2VzdCwgeCA9IH5wb3BkZW5zaXR5LCB5ID0gfnBlcmNvbGxlZ2UpICU+JQogIGFkZF9tYXJrZXJzKGNvbG9yID0gfnN0YXRlLCBjb2xvcnMgPSB2aXJpZGlzOjp2aXJpZGlzKDUpKQpgYGAKCiMgTGluZSBHcmFwaApgYGB7ciBwbG90bHlfbGluZSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDl9CnN0b3Jtc195ZWFybHkgPC0gc3Rvcm1zICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIHN1bW1hcmlzZShudW0gPSBsZW5ndGgodW5pcXVlKG5hbWUpKSkKCnBsb3RfbHkoc3Rvcm1zX3llYXJseSwgeCA9IH55ZWFyLCB5ID0gfm51bSkgJT4lCiAgYWRkX2xpbmVzKCkKYGBgCgojIFlvdXIgVHVybgoxLiBVc2luZyB0aGUgYGdzc19jYXRgIGRhdGEsIGNyZWF0ZSBhIHNjYXR0ZXJwbG90IHNob3dpbmcgdGhlIGBhZ2VgIGFuZCBgdHZob3Vyc2AgdmFyaWFibGVzLgoyLiBDb21wdXRlIHRoZSBhdmVyYWdlIHRpbWUgc3BlbnQgd2F0Y2hpbmcgdHYgYnkgeWVhciBhbmQgbWFyaXRhbCBzdGF0dXMuIFRoZW4sIHBsb3QgdGhlIGF2ZXJhZ2UgdGltZSBzcGVudCB3YXRjaGluZyB0diBieSB5ZWFyIGFuZCBtYXJpdGFsIHN0YXR1cy4KCiMgSGlnaGNoYXJ0ZXI7IEhpZ2hjaGFydHMgZm9yIFIKYGBge3IgaW5zdGFsbF9oaWdoY2hhcnRlciwgZXZhbCA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJoaWdoY2hhcnRlciIpCmBgYAoKIyBgaGNoYXJ0YCBmdW5jdGlvbgpgYGB7ciBoY2hhcnQxLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOX0KbGlicmFyeShoaWdoY2hhcnRlcikKCmxvdHJfY291bnQgPC0gbG90ciAlPiUKICBjb3VudChGaWxtLCBSYWNlKQoKaGNoYXJ0KGxvdHJfY291bnQsICJjb2x1bW4iLCBoY2Flcyh4ID0gUmFjZSwgeSA9IG4sIGdyb3VwID0gRmlsbSkpCmBgYAoKIyBBIHNlY29uZCBgaGNoYXJ0YApgYGB7ciBoY2hhcnQyLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOX0KaGNoYXJ0KG1pZHdlc3QsICJzY2F0dGVyIiwgaGNhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBncm91cCA9IHN0YXRlKSkKYGBgCgojIEhpc3RvZ3JhbQpgYGB7ciBoY2hhcnRfaGlzdCwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDl9CmhjaGFydChsb3RyJFdvcmRzKQpgYGAKCiMgWW91ciBUdXJuCjEuIFVzaW5nIHRoZSBgaGNoYXJ0YCBmdW5jdGlvbiwgY3JlYXRlIGEgYmFyIGNoYXJ0IG9yIGhpc3RvZ3JhbSB3aXRoIHRoZSBgZ3NzX2NhdGAgZGF0YS4KMi4gVXNpbmcgdGhlIGBoY2hhcnRgIGZ1bmN0aW9uLCBjcmVhdGUgYSBzY2F0dGVycGxvdCB3aXRoIHRoZSBgZ3NzX2NhdGAgZGF0YS4KCiMgQnVpbGQgSGlnaGNoYXJ0cyBmcm9tIHNjcmF0Y2gKYGBge3IgaGNfc2NyYXRjaCwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9CmhjIDwtIGhpZ2hjaGFydCgpICU+JQogIGhjX3hBeGlzKGNhdGVnb3JpZXMgPSBsb3RyX2NvdW50JFJhY2UpICU+JQogIGhjX2FkZF9zZXJpZXMobmFtZSA9ICdUaGUgRmVsbG93c2hpcCBPZiBUaGUgUmluZycsIAogICAgICAgICAgICAgICAgZGF0YSA9IGZpbHRlcihsb3RyX2NvdW50LCBGaWxtID09ICdUaGUgRmVsbG93c2hpcCBPZiBUaGUgUmluZycpJG4pICU+JSAKICBoY19hZGRfc2VyaWVzKG5hbWUgPSAnVGhlIFR3byBUb3dlcnMnLCAKICAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIobG90cl9jb3VudCwgRmlsbSA9PSAnVGhlIFR3byBUb3dlcnMnKSRuKSAlPiUKICBoY19hZGRfc2VyaWVzKG5hbWUgPSAnVGhlIFJldHVybiBPZiBUaGUgS2luZycsIAogICAgICAgICAgICAgICAgZGF0YSA9IGZpbHRlcihsb3RyX2NvdW50LCBGaWxtID09ICdUaGUgUmV0dXJuIE9mIFRoZSBLaW5nJykkbikKaGMKYGBgCgojIENoYW5nZSBDaGFydCB0eXBlCmBgYHtyIGhjX2NoYXJ0LCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KaGMgPC0gaGMgJT4lCiAgaGNfY2hhcnQodHlwZSA9ICdjb2x1bW4nKQpoYwpgYGAKCiMgQ2hhbmdlIENvbG9ycwpgYGB7ciBoY19jaGFuZ2VfY29sb3JzLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KaGMgPC0gaGMgJT4lCiAgaGNfY29sb3JzKHN1YnN0cih2aXJpZGlzOjp2aXJpZGlzKDMpLCAwLCA3KSkKaGMKYGBgCgojIE1vZGlmeSBBeGVzCmBgYHtyIGhjX2F4aXMsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA5fQpoYyA8LSBoYyAlPiUKICBoY194QXhpcyh0aXRsZSA9IGxpc3QodGV4dCA9ICJSYWNlIikpICU+JQogIGhjX3lBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIk51bWJlciBvZiBXb3JkcyBTcG9rZW4iKSwKICAgICAgICAgICBzaG93TGFzdExhYmVsID0gRkFMU0UpCmhjCmBgYAoKIyBBZGQgdGl0bGUsIHN1YnRpdGxlLCBtb3ZlIGxlZ2VuZApgYGB7ciBoY19tb2RpZnksIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA5fQpoYyA8LSBoYyAlPiUKICBoY190aXRsZSh0ZXh0ID0gJ051bWJlciBvZiBXb3JkcyBTcG9rZW4gaW4gTG9yZCBvZiB0aGUgUmluZ3MgRmlsbXMnLAogICAgICAgICAgIGFsaWduID0gJ2xlZnQnKSAlPiUKICBoY19zdWJ0aXRsZSh0ZXh0ID0gJ0Jyb2tlbiBkb3duIGJ5IDxpPkZpbG08L2k+IGFuZCA8Yj5SYWNlPC9iPicsIAogICAgICAgICAgICAgIGFsaWduID0gJ2xlZnQnKSAlPiUKICBoY19sZWdlbmQoYWxpZ24gPSAncmlnaHQnLCB2ZXJ0aWNhbEFsaWduID0gJ3RvcCcsIGxheW91dCA9ICd2ZXJ0aWNhbCcsCiAgICAgICAgICAgIHggPSAwLCB5ID0gODApICU+JQogIGhjX2V4cG9ydGluZyhlbmFibGVkID0gVFJVRSkKaGMKYGBgCgoKIyBZb3VyIFR1cm4KMS4gQnVpbGQgdXAgYSBwbG90IGZyb20gc2NyYXRjaCwgZ2V0dGluZyB0aGUgZmlndXJlIGNsb3NlIHRvIHB1YmxpY2F0aW9uIHF1YWxpdHkgdXNpbmcgdGhlIGBnc3NfY2F0YCBkYXRhLgoKIyBDb3JyZWxhdGlvbiBNYXRyaWNlcwpgYGB7ciBjb3JyZWxhdGlvbn0Kc2VsZWN0KHN0b3Jtcywgd2luZCwgcHJlc3N1cmUsIHRzX2RpYW1ldGVyLCBodV9kaWFtZXRlcikgJT4lCiAgY29yKHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSAlPiUKICBoY2hhcnQoKQpgYGAKCiMgTGVhZmxldCBFeGFtcGxlCmBgYHtyIGxlYWZsZXR9CmxpYnJhcnkobGVhZmxldCkKCnN0b3JtcyAlPiUKICBmaWx0ZXIobmFtZSAlaW4lIGMoJ0lrZScsICdLYXRyaW5hJyksIHllYXIgPiAyMDAwKSAlPiUKICBsZWFmbGV0KCkgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRDaXJjbGVzKGxuZyA9IH5sb25nLCBsYXQgPSB+bGF0LCBwb3B1cCA9IH5uYW1lLCB3ZWlnaHQgPSAxLAogICAgICAgICAgICAgcmFkaXVzID0gfndpbmQqMTAwMCkKYGBgCgojIGdnYW5pbWF0ZQpgYGB7ciBnZ2FuaW1hdGUsIGV2YWwgPSBGQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiZ2dhbmltYXRlIikKYGBgCgpgYGB7ciBnZ2FuaW1hdGUtZXhhbXBsZX0KbGlicmFyeShnZ2FuaW1hdGUpCgpnZ3Bsb3Qoc3Rvcm1zLCBhZXMoeCA9IHByZXNzdXJlLCB5ID0gd2luZCwgY29sb3IgPSBzdGF0dXMpKSArIAogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB4bGFiKCJQcmVzc3VyZSIpICsgCiAgeWxhYigiV2luZCBTcGVlZCAoTVBIKSIpICsKICBmYWNldF93cmFwKH5zdGF0dXMpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkgKwogIGxhYnModGl0bGUgPSAnWWVhcjoge2ZyYW1lX3RpbWV9JykgKwogIHRyYW5zaXRpb25fdGltZShhcy5pbnRlZ2VyKHllYXIpKSArIAogIGVhc2VfYWVzKCdsaW5lYXInKQpgYGAKCmBgYHtyIHNhdmUtYW5pbWF0aW9ufQphbmltX3NhdmUoZmlsZW5hbWUgPSAnc3Rvcm1zLmdpZicpCmBgYAoKIVtdKHN0b3Jtcy5naWYpCgoKIyBBZGRpdGlvbmFsIFJlc291cmNlcwoqIHBsb3RseSBmb3IgUiBib29rOiA8aHR0cHM6Ly9wbG90bHktYm9vay5jcHNpZXZlcnQubWUvPgoqIHBsb3RseTogPGh0dHBzOi8vcGxvdC5seS8+CiogaGlnaGNoYXJ0ZXI6IDxodHRwOi8vamt1bnN0LmNvbS9oaWdoY2hhcnRlci9pbmRleC5odG1sPgoqIGhpZ2hjaGFydHM6IDxodHRwczovL3d3dy5oaWdoY2hhcnRzLmNvbS8+CiogaHRtbHdpZGdldHM6IDxodHRwczovL3d3dy5odG1sd2lkZ2V0cy5vcmcvPgoqIGdnYW5pbWF0ZTogPGh0dHBzOi8vZ2dhbmltYXRlLmNvbS8+Cg==